#include	"compiler.h"
#include	"strres.h"
#include	"scrnmng.h"
#include	"cpucore.h"
#include	"pccore.h"
#include	"iocore.h"
#include	"sound.h"
#include	"fmboard.h"
#include	"np2info.h"


static const char str_comma[] = ", ";
static const char str_2halfMHz[] = "2.5MHz";
#define str_5MHz	(str_2halfMHz + 2)
static const char str_8MHz[] = "8MHz";
static const char str_notexist[] = "not exist";
static const char str_disable[] = "disable";

static const char str_cpu[] =
						"8086-2\0"					\
						"70116\0"					\
						"80286\0"					\
						"80386\0"					\
						"80486\0"					\
						"Pentium\0"					\
						"PentiumPro";
static const char str_winclr[] =
						"256-colors\0"				\
						"65536-colors\0"			\
						"full color\0"				\
						"true color";
static const char str_winmode[] =
						" (window)\0"				\
						" (fullscreen)";
static const char str_grcgchip[] =
						"\0"						\
						"GRCG \0"					\
						"GRCG CG-Window \0"			\
						"EGC CG-Window ";
static const char str_vrammode[] =
						"Digital\0"					\
						"Analog\0"					\
						"256colors";
static const char str_vrampage[] =
						" page-0\0"					\
						" page-1\0"					\
						" page-all";
static const char str_chpan[] =
						"none\0"					\
						"Mono-R\0"					\
						"Mono-L\0"					\
						"Stereo";
static const char str_fmboard[] =
						"none\0"					\
						"PC-9801-14\0"				\
						"PC-9801-26\0"				\
						"PC-9801-86\0"				\
						"PC-9801-26 + 86\0"			\
						"PC-9801-118\0"				\
						"PC-9801-86 + Chibi-oto\0"	\
						"Speak board\0"				\
						"Spark board\0"				\
						"AMD-98";

static const char str_clockfmt[] = "%d.%1dMHz";
static const char str_memfmt[] = "%3uKB";
static const char str_memfmt2[] = "%3uKB + %uKB";
static const char str_memfmt3[] = "%d.%1dMB";
static const char str_width[] = "width-%u";
static const char str_dispclock[] = "%u.%.2ukHz / %u.%uHz";

static const char str_pcm86a[] = "   PCM: %dHz %dbit %s";
static const char str_pcm86b[] = "        %d / %d / 32768";
static const char str_rhythm[] = "BSCHTR";


// ---- common

static void info_ver(char *str, int maxlen, NP2INFOEX *ex) {

	milstr_ncpy(str, np2version, maxlen);
	(void)ex;
}

static void info_cpu(char *str, int maxlen, NP2INFOEX *ex) {

	UINT	family;

#if defined(CPU_FAMILY)
	family = min(CPU_FAMILY, 6);
#else
	family = (CPU_TYPE & CPUTYPE_V30)?1:2;
#endif
	milstr_ncpy(str, milstr_list(str_cpu, family), maxlen);
	(void)ex;
}

static void info_clock(char *str, int maxlen, NP2INFOEX *ex) {

	UINT32	clock;
	char	clockstr[16];

	clock = (pccore.realclock + 50000) / 100000;
	SPRINTF(clockstr, str_clockfmt, clock/10, clock % 10);
	milstr_ncpy(str, clockstr, maxlen);
	(void)ex;
}

static void info_base(char *str, int maxlen, NP2INFOEX *ex) {

	milstr_ncpy(str,
				(pccore.cpumode & CPUMODE_8MHZ)?str_8MHz:str_5MHz, maxlen);
	(void)ex;
}

static void info_mem1(char *str, int maxlen, NP2INFOEX *ex) {

	UINT	memsize;
	char	memstr[32];

	memsize = np2cfg.memsw[2] & 7;
	if (memsize < 6) {
		memsize = (memsize + 1) * 128;
	}
	else {
		memsize = 640;
	}
	if (pccore.extmem) {
		SPRINTF(memstr, str_memfmt2, memsize, pccore.extmem * 1024);
	}
	else {
		SPRINTF(memstr, str_memfmt, memsize);
	}
	milstr_ncpy(str, memstr, maxlen);
	(void)ex;
}

static void info_mem2(char *str, int maxlen, NP2INFOEX *ex) {

	UINT	memsize;
	char	memstr[16];

	memsize = np2cfg.memsw[2] & 7;
	if (memsize < 6) {
		memsize = (memsize + 1) * 128;
	}
	else {
		memsize = 640;
	}
	memsize += pccore.extmem * 1024;
	SPRINTF(memstr, str_memfmt, memsize);
	milstr_ncpy(str, memstr, maxlen);
	(void)ex;
}

static void info_mem3(char *str, int maxlen, NP2INFOEX *ex) {

	UINT	memsize;
	char	memstr[16];

	memsize = np2cfg.memsw[2] & 7;
	if (memsize < 6) {
		memsize = (memsize + 1) * 128;
	}
	else {
		memsize = 640;
	}
	if (pccore.extmem > 1) {
		SPRINTF(memstr, str_memfmt3, pccore.extmem, memsize / 100);
	}
	else {
		SPRINTF(memstr, str_memfmt, memsize);
	}
	milstr_ncpy(str, memstr, maxlen);
	(void)ex;
}

static void info_gdc(char *str, int maxlen, NP2INFOEX *ex) {

	milstr_ncpy(str, milstr_list(str_grcgchip, grcg.chip & 3), maxlen);
	milstr_ncat(str, str_2halfMHz + ((gdc.clock & 0x80)?2:0), maxlen);
	(void)ex;
}

static void info_gdc2(char *str, int maxlen, NP2INFOEX *ex) {

	char	textstr[32];

	SPRINTF(textstr, str_dispclock,
						gdc.hclock / 1000, (gdc.hclock / 10) % 100,
						gdc.vclock / 10, gdc.vclock % 10);
	milstr_ncpy(str, textstr, maxlen);
	(void)ex;
}

static void info_text(char *str, int maxlen, NP2INFOEX *ex) {

const char	*p;
	char	textstr[64];

	if (!(gdcs.textdisp & GDCSCRN_ENABLE)) {
		p = str_disable;
	}
	else {
		SPRINTF(textstr, str_width, ((gdc.mode1 & 0x4)?40:80));
		p = textstr;
	}
	milstr_ncpy(str, p, maxlen);
	(void)ex;
}

static void info_grph(char *str, int maxlen, NP2INFOEX *ex) {

const char	*p;
	UINT	md;
	UINT	pg;
	char	grphstr[32];

	if (!(gdcs.grphdisp & GDCSCRN_ENABLE)) {
		p = str_disable;
	}
	else {
		md = (gdc.analog & (1 << GDCANALOG_16))?1:0;
		pg = gdcs.access;
#if defined(SUPPORT_PC9821)
		if (gdc.analog & (1 << (GDCANALOG_256))) {
			md = 2;
			if (gdc.analog & (1 << (GDCANALOG_256E))) {
				pg = 2;
			}
		}
#endif
		milstr_ncpy(grphstr, milstr_list(str_vrammode, md), sizeof(grphstr));
		milstr_ncat(grphstr, milstr_list(str_vrampage, pg), sizeof(grphstr));
		p = grphstr;
	}
	milstr_ncpy(str, p, maxlen);
	(void)ex;
}

static void info_sound(char *str, int maxlen, NP2INFOEX *ex) {

	UINT	type;

	type = 0;
	switch(usesound) {
		case 0x01:
			type = 1;
			break;

		case 0x02:
			type = 2;
			break;

		case 0x04:
			type = 3;
			break;

		case 0x06:
			type = 4;
			break;

		case 0x08:
			type = 5;
			break;

		case 0x14:
			type = 6;
			break;

		case 0x20:
			type = 7;
			break;

		case 0x40:
			type = 8;
			break;

		case 0x80:
			type = 9;
			break;
	}
	milstr_ncpy(str, milstr_list(str_fmboard, type), maxlen);
	(void)ex;
}

static void info_extsnd(char *str, int maxlen, NP2INFOEX *ex) {

	char	buf[64];

	info_sound(str, maxlen, ex);
	if (usesound & 4) {
		milstr_ncat(str, ex->cr, maxlen);
		SPRINTF(buf, str_pcm86a,
							pcm86rate8[pcm86.fifo & 7] >> 3,
							(16 - ((pcm86.dactrl >> 3) & 8)),
							milstr_list(str_chpan, (pcm86.dactrl >> 4) & 3));
		milstr_ncat(str, buf, maxlen);
		milstr_ncat(str, ex->cr, maxlen);
		SPRINTF(buf, str_pcm86b, pcm86.virbuf, pcm86.fifosize);
		milstr_ncat(str, buf, maxlen);
	}
}

static void info_bios(char *str, int maxlen, NP2INFOEX *ex) {

	str[0] = '\0';
	if (pccore.rom & PCROM_BIOS) {
		milstr_ncat(str, str_biosrom, maxlen);
	}
	if (soundrom.name[0]) {
		if (str[0]) {
			milstr_ncat(str, str_comma, maxlen);
		}
		milstr_ncat(str, soundrom.name, maxlen);
	}
	if (str[0] == '\0') {
		milstr_ncat(str, str_notexist, maxlen);
	}
	(void)ex;
}

static void info_rhythm(char *str, int maxlen, NP2INFOEX *ex) {

	char	rhythmstr[8];
	UINT	exist;
	UINT	i;

	exist = rhythm_getcaps();
	milstr_ncpy(rhythmstr, str_rhythm, sizeof(rhythmstr));
	for (i=0; i<6; i++) {
		if (!(exist & (1 << i))) {
			rhythmstr[i] = '_';
		}
	}
	milstr_ncpy(str, rhythmstr, maxlen);
	(void)ex;
}

static void info_display(char *str, int maxlen, NP2INFOEX *ex) {

	UINT	bpp;

	bpp = scrnmng_getbpp();
	milstr_ncpy(str, milstr_list(str_winclr, ((bpp >> 3) - 1) & 3), maxlen);
	milstr_ncat(str, milstr_list(str_winmode, (scrnmng_isfullscreen())?1:0),
																	maxlen);
	(void)ex;
}


// ---- make string

typedef struct {
	char	key[8];
	void	(*proc)(char *str, int maxlen, NP2INFOEX *ex);
} INFOPROC;

static const INFOPROC infoproc[] = {
			{"VER",			info_ver},
			{"CPU",			info_cpu},
			{"CLOCK",		info_clock},
			{"BASE",		info_base},
			{"MEM1",		info_mem1},
			{"MEM2",		info_mem2},
			{"MEM3",		info_mem3},
			{"GDC",			info_gdc},
			{"GDC2",		info_gdc2},
			{"TEXT",		info_text},
			{"GRPH",		info_grph},
			{"SND",			info_sound},
			{"EXSND",		info_extsnd},
			{"BIOS",		info_bios},
			{"RHYTHM",		info_rhythm},
			{"DISP",		info_display}};


static BOOL defext(char *dst, const char *key, int maxlen, NP2INFOEX *ex) {

	milstr_ncpy(dst, key, maxlen);
	(void)ex;
	return(TRUE);
}

void np2info(char *dst, const char *src, int maxlen, const NP2INFOEX *ex) {

	NP2INFOEX	statex;
	char		c;
	int			leng;
	char		infwork[12];
const INFOPROC	*inf;
const INFOPROC	*infterm;

	if ((dst == NULL) || (maxlen <= 0) || (src == NULL)) {
		return;
	}
	if (ex == NULL) {
		milstr_ncpy(statex.cr, str_oscr, sizeof(statex.cr));
		statex.ext = NULL;
	}
	else {
		statex = *ex;
	}
	if (statex.ext == NULL) {
		statex.ext = defext;
	}
	while(maxlen > 0) {
		c = *src++;
		if (c == '\0') {
			break;
		}
		else if (c == '\n') {
			milstr_ncpy(dst, statex.cr, maxlen);
		}
		else if (c != '%') {
			*dst++ = c;
			maxlen--;
			continue;
		}
		else if (*src == '%') {
			src++;
			*dst++ = c;
			maxlen--;
			continue;
		}
		else {
			leng = 0;
			while(1) {
				c = *src;
				if (c == '\0') {
					break;
				}
				src++;
				if (c == '%') {
					break;
				}
				if (leng < (int)(sizeof(infwork) - 1)) {
					infwork[leng++] = c;
				}
			}
			infwork[leng] = '\0';
			inf = infoproc;
			infterm = infoproc + (sizeof(infoproc) / sizeof(INFOPROC));
			while(inf < infterm) {
				if (!milstr_cmp(infwork, inf->key)) {
					inf->proc(dst, maxlen, &statex);
					break;
				}
				inf++;
			}
			if (inf >= infterm) {
				if (!(*statex.ext)(dst, infwork, maxlen, &statex)) {
					continue;
				}
			}
		}
		leng = strlen(dst);
		dst += leng;
		maxlen -= leng;
	}
	*dst = '\0';
}

